// Ninf-G-XX, ver. 0.1
// Copyright 2011 National Institute of Advanced Industrial Science and Technology
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <map>
#include <string>
#include <vector>
#include "grpc_data.h"
#include "GrpcData.hpp"
#include "GrpcDataImpl.hpp"
#include "GrpcException.hpp"


static bool _grpc_data_initialized(grpc_data_t * data){
  return
  ! (find(ninf::data::grpcDataVector.begin(),
          ninf::data::grpcDataVector.end(), data)
          == ninf::data::grpcDataVector.end());
}
static void _grpc_data_remove_from_list(grpc_data_t * data){
  std::vector<void *>::iterator result =
      find(ninf::data::grpcDataVector.begin(),
          ninf::data::grpcDataVector.end(), data);
  if (result != ninf::data::grpcDataVector.end())
    ninf::data::grpcDataVector.erase(result);
}

/**
 * initialize function for data handles
 * - data should be allocated by users
 * - list of URIs are NULL terminated list
 * - variable_configuration will be 0 terminated array
 *   like [3, 3, 0]
 */

grpc_error_t
grpc_data_init(
  grpc_data_t * const data,
  const char ** const list_of_URI_input,
  const char ** const list_of_URI_output,
  const grpc_data_type_t variable_type,
  const size_t* const variable_configuration,
  const grpc_data_mode_t * storage_modes
) {
    data->realhandle = new ninf::data::GrpcData(
        list_of_URI_input,
        list_of_URI_output,
        variable_type,
        variable_configuration,
        storage_modes
    );
    ninf::data::grpcDataVector.push_back(data);
    return GRPC_NO_ERROR;
}

grpc_error_t
grpc_data_unbind(
  grpc_data_t * data
) {
  if (!_grpc_data_initialized(data))
    return GRPC_INVALID_DATA;  // the data is not initialized
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
  delete gdata;
  data->realhandle = NULL; // NULL clear to avoid strange errors..
  _grpc_data_remove_from_list(data);
  return GRPC_NO_ERROR;
}

grpc_error_t
grpc_data_free(
  grpc_data_t * data,
  char ** URI_locations
) {
  if (!_grpc_data_initialized(data))
    return GRPC_INVALID_DATA;  // the data is not initialized
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
//  gdata.free(char ** URI_locations); // tries to do something;
  delete gdata;
  data->realhandle = NULL; // NULL clear to avoid strange errors..
  _grpc_data_remove_from_list(data);
  return GRPC_NO_ERROR;
}

grpc_error_t
grpc_data_getinfo(
  grpc_data_t * data,
  grpc_data_info_type_t info_tag,
  const char * server_name,
  char ** info
) {
  if (!_grpc_data_initialized(data))
    return GRPC_INVALID_DATA;  // the data is not initialized
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
  return gdata->getInfo(info_tag, server_name, info);
}

grpc_error_t
grpc_data_memory_mapping_set(
  const char * key,
  void       * ptr
) {
  ninf::data::mappingManager.put(key, ptr);
  return GRPC_NO_ERROR;
}

grpc_error_t
grpc_data_memory_mapping_get(
  const char * key,
  void      ** ptr
) {
  *ptr = ninf::data::mappingManager.get(key);
  if (*ptr == NULL)
     return GRPC_INVALID_ARGUMENT;
  return GRPC_NO_ERROR;
}

/**
 * read grpc_data_t metadata into a file?
 *
 */

grpc_error_t
grpc_data_load(
  grpc_data_t * data,
  const char * URI_input
) {
  if (!_grpc_data_initialized(data)) {
    data->realhandle = new ninf::data::GrpcData();
    ninf::data::grpcDataVector.push_back(data);
  }
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
  try {
    gdata->load(URI_input);
  } catch (ninf::lib::GrpcException e) {
    std::cerr << e.message << std::endl;
    return e.errorCode;
  }
  return GRPC_NO_ERROR;
}

/**
 * write data into the grpc_data_t. if the input_URL list
 * includes memory, the content will be dumped.
 * if input_URLs contains external URLs only,
 * URL to URL copy will be performed.
 */
grpc_error_t
grpc_data_save(
  const grpc_data_t * data,
  const char * URI_output
){
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
  try {
    gdata->save(URI_output);
  } catch (ninf::lib::GrpcException e) {
    std::cerr << e.message << std::endl;
    return e.errorCode;
  }
  return GRPC_NO_ERROR;

}

grpc_error_t
grpc_data_transfer(
  grpc_data_t * data,
  const char ** list_of_input_URI,
  const char ** list_of_output_URI,
  const grpc_data_mode_t * list_of_input_modes,
  const grpc_data_mode_t * list_of_output_modes
) {
  ninf::data::GrpcData * gdata =
      (ninf::data::GrpcData *)data->realhandle;
  try {
    gdata->transfer(
          list_of_input_URI,
          list_of_output_URI,
          list_of_input_modes,
          list_of_output_modes);
  } catch (ninf::lib::GrpcException e) {
    std::cerr << e.message << std::endl;
    return e.errorCode;
  }
  return GRPC_NO_ERROR;
}

